local prolog = [[
	require "bit"

	local bor = bit.bor
	local band = bit.band
	local lshift = bit.lshift
	local rshift = bit.rshift
	local tobit = bit.tobit
	local abs = math.abs
]]

function obitstream_factory(before, putbuf, after)
	local src = prolog .. [[
		local function create()
			local table = {}

			local out_bit_count = 0
			local out_buf = 0

			]]..before..[[

			function table.putint(_i, _bits)
				local _bit_count = out_bit_count
				local buf = bor(out_buf, lshift(_i, _bit_count))

				_bit_count = _bit_count + _bits

				if _bit_count >= 32 then
					]]..putbuf..[[
					_bit_count = _bit_count - 32
					local _consumed_bits = _bits - _bit_count -- [1, 32]
					buf = rshift(rshift(_i, 1), _consumed_bits - 1)
				end

				out_buf = buf
				out_bit_count = _bit_count
			end

			function table.out_finish()
				if out_bit_count ~= 0 then
					local buf = out_buf
					]]..putbuf..[[
					out_buf = 0
					out_bit_count = 0
				end
			end

			function table.v_int(v)
				table.putint(v, 32)
				return v
			end

			function table.int(t, i)
				table.putint(t[i], 32)
			end

			function table.obj_typed(t, i, proto)
				proto.archive(t[i], table)
			end

			]]..after..[[

			return table
		end

		return create
	]]

	return assert(loadstring(src, "obitstream"))()
end

function ibitstream_factory(before, getbuf, after)
	local src = prolog .. [[
		local function create()
			local table = {}

			local in_bit_count = 32
			local in_buf = 0

			]]..before..[[

			function table.getint(_bits)
				local _read = in_bit_count -- [1, 32]
				
				local _new_read = _read + _bits
				local _v
				local buf = in_buf

				if _new_read <= 32 then
					in_bit_count = _new_read
					_v = rshift(buf, _read)
				else
					_v = rshift(rshift(buf, 1), _read - 1)
					local _written = 32 - _read  -- Number of bits acquired in v
					
					local bits_in_buf = 32

					in_bit_count = _new_read
					while true do
						]]..getbuf..[[

						in_bit_count = in_bit_count - bits_in_buf
						if in_bit_count <= 32 then break end
						_v = bor(_v, lshift(buf, _written))
						_written = _written + bits_in_buf
					end

					_v = bor(_v, lshift(buf, _written))
					in_buf = lshift(buf, 32 - bits_in_buf) -- Adjust for missing bits
				end

				local _shift = 32 - _bits
				return rshift(lshift(_v, _shift), _shift)
			end

			function table.in_reset()
				in_bit_count = 32
				in_buf = 0
			end

			function table.v_int(v)
				return table.getint(32)
			end

			function table.int(t, i)
				t[i] = table.getint(32)
			end

			function table.obj_typed(t, i, proto)
				local o = proto:create()
				o:archive(table)
				t[i] = o
			end

			]]..after..[[

			return table
		end

		return create
	]]

	return assert(loadstring(src, "ibitstream"))()
end